home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Power Programmierung
/
Power-Programmierung (Tewi)(1994).iso
/
magazine
/
drdobbs
/
1987
/
03
/
dunc.lst
next >
Wrap
File List
|
1987-02-06
|
18KB
|
661 lines
* paste -- a program to attach to the lines of a file the correspond- *
* ing lines of another file, with an optional string between *
* them. *
* *
* Written January, 1984 by John M. Gamble *
* Updated for UNIX April, 1986 *
* *
* usage: *
* *
* paste [-paste] [-b <string>] [-<n>] [file1] [file2] *
* *
* options: *
* *
* -p <file1> does not exist (<string> is prepended to each *
* line.) *
* *
* -a <file2> does not exist (<string> is appended to each *
* line.) *
* *
* -s Do not print <string> with lines from only one file. *
* *
* -t An option to resolve the ambiguous command *
* "paste <file>". The -t flag forces <file> to trail *
* standard input. I.e., *
* *
* "paste <file>" *
* is equivalent to "paste <file> <stdin>" *
* *
* "paste -t <file>" *
* is equivalent to "paste <stdin> <file>". *
* *
* -e Do not print <string> if both input lines are empty *
* (i.e., that consist of no characters but '\n'.) *
* *
* -b <string> A string of characters to be inserted between the lines *
* of <file1> and <file2>. The string may contain all *
* the standard escape codes with the exception of '\0'. *
* The string may also indicate blanks with the escape *
* sequence '\s'. *
* *
* -<n> Print <n> lines of <file1> before appending lines of *
* <file2>. If <n> is negative (e.g., "paste --3") then *
* <n> lines of <file2> will be printed first. *
*----------------------------------------------------------------------*/
#include <stdio.h>
#include <ctype.h>
/* On systems such as UNIX, if a string with blanks in it is
* surrounded by quote marks, it is considered to be one string.
* On other systems, the blank ends the string and the quote
* marks are passed along with the other characters. So, while
* on UNIX, the command
*
* 'paste -b "; do " list1 list2'
*
* would set
* argv[2] to "; do ",
* argv[3] to list1,
* argv[3] to list2.
*
* a system like MSDOS would set
* argv[2] to "\";",
* argv[3] to "do",
* argv[4] to "\"",
* argv[5] to list1,
* argv[6] to list2.
*
* This is easily taken care of, but it does mean that conditional
* compilation is required by setting the switch below to either
* zero or one, depending on your particular operating system.
*/
#define BLANK_ENDS_STR 0
#define STRINGLEN 128
#define TRUE 1
#define FALSE 0
#define isoctal(x) ((x) >= '0' && (x) < '8')
typedef unsigned int Boolean;
char bstring[STRINGLEN] = {'\0'};
char *nullstr = "";
char *strf = "%s";
char *program_name = "paste";
char *error_msg[] =
{
/*0*/ "usage: %s [-aptse] [-b \"string\"] [-<n>] [file1] [file2] %s\n",
/*1*/ "%s: unknown flag %s\n",
/*2*/ "%s: at least one file must exist%s\n",
/*3*/ "%s: -t flag is only valid with one file on the command line%s\n",
/*4*/ "%s: both files can't be standard input%s\n",
/*5*/ "%s: contradictory options%s\n",
/*6*/ "%s: can't open %s\n",
/*7*/ "%s: -a or -p flags are invalid with two files%s\n",
/*8*/ "%s: too many files%s\n",
/*9*/ "%s: string argument lacks closing \' or \"%s\n",
/*10*/ "%s: null character in string argument%s\n",
/*11*/ "%s: string argument too long%s\n"
};
main(argc, argv)
int argc;
char **argv;
{
FILE *fp1, *fp2, *fopen();
Boolean prepend = FALSE, append = FALSE, trailing = FALSE;
Boolean printempty = TRUE, printsingle = TRUE;
int slip = 0;
char *subarg;
if (argc == 1)
exit_error(0, nullstr);
/* Get the flags.
*/
while (--argc > 0 && **++argv == '-')
{
switch (*(*argv + 1))
{
case '\0': /* Because default: won't catch this.*/
exit_error(1, *argv);
break;
case 'b':
if (argc == 1)
exit_error(0, nullstr);
argc--;
argv++;
#if BLANK_ENDS_STR
strget(&argc, &argv, bstring);
#else
strload(*argv, bstring);
#endif
break;
case '-':
case '0':
case '1':
case '2':
case '3':
case '4':
case '5':
case '6':
case '7':
case '8':
case '9':
slip = atoi(*argv + 1);
break;
default:
subarg = *argv;
while (*++subarg)
{
switch (*subarg)
{
case 'a':
append = TRUE;
break;
case 'e':
printempty = FALSE;
break;
case 'p':
prepend = TRUE;
break;
case 's':
printsingle = FALSE;
break;
case 't':
trailing = TRUE;
break;
default:
exit_error(1, *argv);
break;
}
break;
}
}
}
if (prepend && append) /* Contradictory options.*/
exit_error(2, nullstr);
switch (argc) /* The number of file names on the command line.*/
{
case 0:
if (trailing)
exit_error(3, nullstr);
if (!(prepend || append)) /* Both files can't be stdin.*/
exit_error(4, nullstr);
if (append)
attachf(stdin, NULL, slip, printsingle, printempty);
else
attachf(NULL, stdin, slip, printsingle, printempty);
break;
case 1:
/* Contradictory options?
*/
if (trailing && (prepend || append))
exit_error(5, nullstr);
if ((fp1 = fopen(*argv, "r")) == NULL)
exit_error(6, *argv);
if (append)
attachf(fp1, NULL, slip, printsingle, printempty);
else if (prepend)
attachf(NULL, fp1, slip, printsingle, printempty);
else if (trailing)
attachf(stdin, fp1, slip, printsingle, printempty);
else
attachf(fp1, stdin, slip, printsingle, printempty);
fclose(fp1);
break;
case 2:
if (trailing)
exit_error(3, nullstr);
if (prepend || append)
exit_error(7, nullstr);
if ((fp1 = fopen(*argv, "r")) == NULL)
exit_error(6, *argv);
if ((fp2 = fopen(*++argv, "r")) == NULL)
exit_error(6, *argv);
attachf(fp1, fp2, slip, printsingle, printempty);
fclose(fp1);
fclose(fp2);
break;
default:
exit_error(8, nullstr);
break;
}
exit(0);
} /* End of main.*/
#if BLANK_ENDS_STR
/*----------------------------------------------------------------------*
* strget -- retrieve the <string> argument from the command line. *
* If the string contains blanks, C assumes this is the end of the *
* string, and places a \0 at its end. Since WE know that it's *
* just a blank, we put one in, update the position of argv, and *
* decrement argc. Escape sequences are treated just as defined *
* in C (except \0, which is an error). One extra escape sequence *
* ('\s') exists to handle multiple blanks on a line, for even if *
* the string is enclosed in quotes the extra blanks will not be *
* passed from the command line. *
*----------------------------------------------------------------------*/
strget(pargc, pargv, bstr)
int *pargc;
char ***pargv;
char *bstr;
{
register int j;
char *subarg;
Boolean st_quote = FALSE;
Boolean st_apost = FALSE;
subarg = **pargv;
/* If the string is begun with a quote or an apostrophe, remember
* so that we know when to end the string.
*/
if ((st_quote = (*subarg == '"')) || (st_apost = (*subarg == '\'')))
subarg++;
for (j = 0; j < STRINGLEN; bstr++, subarg++, j++)
{
/* A `"` or `'` encountered could mean the end of a string -
* check against st_quote or st_apost.
*/
if ((st_quote && *subarg == '"') ||
(st_apost && *subarg == '\''))
break;
else if (*subarg == '\0') /* Blank encountered in string.*/
{
/* If we began with a quote, we are not finished.
*/
if (st_quote || st_apost)
{
/* If nothing is left on the command line,
* a quote mark is missing.
*/
if (--(*pargc) == 0)
exit_error(9, nullstr);
/* Put the blank in, and point subarg
* to the next argv string.
*/
*bstr = ' ';
subarg = *(++(*pargv)) - 1;
}
/* Otherwise we didn't start with a quote mark - end.
*/
else
break;
}
else if (*subarg == '\\') /* Escape sequences.*/
switch(*++subarg)
{
/* Nothing after the '\' - let the 'blank'
* section handle it.
*/
case '\0':
bstr--;
subarg--;
j--;
break;
case '"':
*bstr = '"';
break;
case '0':
case '1':
case '2':
case '3':
case '4':
case '5':
case '6':
case '7':
*bstr = (char) bit_pattern(&subarg);
break;
case '\\':
*bstr = '\\';
break;
case 'b':
*bstr = '\b';
break;
case 'f':
*bstr = '\f';
break;
case 'n':
*bstr = '\n';
break;
case 'r':
*bstr = '\r';
break;
case 't':
*bstr = '\t';
break;
case 's':
*bstr = ' ';
break;
default:
*bstr = *subarg;
break;
}
else
*bstr = *subarg; /* No special character handling.*/
}
if (j == STRINGLEN)
exit_error(11, nullstr);
*bstr = '\0';
} /* End of strget.*/
#else
/*----------------------------------------------------------------------*
* strload -- retrieve the <string> argument from the command line. *
* Escape sequences are treated just as defined in C (except \0, *
* which is an error). One extra escape sequence ('\s') exists in *
* order to handle multiple blanks on a line without bothering to *
* enclose the string in quotes. *
*----------------------------------------------------------------------*/
strload(subarg, bstr)
char *subarg;
char *bstr;
{
extern char *nullstr;
register int j;
for (j = 0; *subarg && j < STRINGLEN; bstr++, subarg++, j++)
if (*subarg == '\\') /* Escape sequences.*/
switch(*++subarg)
{
case '0':
case '1':
case '2':
case '3':
case '4':
case '5':
case '6':
case '7':
*bstr = (char) bit_pattern(&subarg);
break;
case '\\':
*bstr = '\\';
break;
case 'b':
*bstr = '\b';
break;
case 'f':
*bstr = '\f';
break;
case 'n':
*bstr = '\n';
break;
case 'r':
*bstr = '\r';
break;
case 't':
*bstr = '\t';
break;
case 's':
*bstr = ' ';
break;
default:
*bstr = *subarg;
break;
}
else
*bstr = *subarg; /* No special character handling.*/
if (j == STRINGLEN)
exit_error(11, nullstr);
*bstr = '\0';
} /* End of strload.*/
#endif
/*----------------------------------------------------------------------*
* bit_pattern -- Change the \ddd format to a character symbol. It will *
* check to see if there are (at most two) other octal digits *
* present. It does not allow the return of the null character. *
* The pointer *ddd is only incremented by one for each extra *
* digit, because the pointer will be incremented again upon *
* returning from the function. *
*----------------------------------------------------------------------*/
bit_pattern(ddd)
char **ddd;
{
extern char *nullstr;
int num;
num = **ddd - '0'; /* Num is octal, otherwise we wouldn't be here.*/
if (isoctal(*(*ddd + 1))) /* Is the next character an octal digit?*/
{
num = 8 * num + *++*ddd - '0';
if (isoctal(*(*ddd +1))) /* How about this character?*/
num = 8 * num + *++*ddd - '0';
}
if (!num)
exit_error(10, nullstr); /* No \0 allowed.*/
return(num);
} /* End of bit_pattern.*/
/*----------------------------------------------------------------------*
* attachf -- Take the lines of <file2>, if any, and attach them *
* to the lines of <file1>, if any. Slip determines how many *
* lines of <file1> (<file2> if negative) are printed before *
* printing the lines from both files together. It is possible to *
* specify some slippage even if the -a or -p flags are present. *
* This is not an error. Attachf is smart enough to skip slip *
* in that case. *
*----------------------------------------------------------------------*/
attachf(fp1, fp2, slip, printsingle, printempty)
FILE *fp1, *fp2;
int slip;
Boolean printsingle, printempty;
{
Boolean notempty;
register int nxtc;
/* Handle slippage, if any, up to the end of the file.
*/
for (; slip > 0 && fp1 != NULL; slip--)
{
notempty = (nxtc = nextc(fp1)) != '\n';
if (nxtc == EOF)
{
fp1 = NULL;
break;
}
put_line(fp1);
if (printsingle && (printempty || notempty))
printf(strf, bstring);
putchar('\n');
}
if (slip < 0)
slip = -slip;
for (; slip > 0 && fp2 != NULL; slip--)
{
if ((nxtc = nextc(fp2)) == EOF)
{
fp2 = NULL;
break;
}
if (printsingle && (printempty || nxtc != '\n'))
printf(strf, bstring);
put_line(fp2);
putchar('\n');
}
/* Paste the lines of each file together.
*/
while (fp1 != NULL && fp2 != NULL)
{
notempty = (nxtc = nextc(fp1)) != '\n';
if (nxtc == EOF)
{
fp1 = NULL;
break;
}
put_line(fp1);
if (printempty || notempty || nextc(fp2) != '\n')
printf(strf, bstring);
put_line(fp2);
if (nextc(fp2) == EOF)
{
fp2 = NULL;
}
putchar('\n');
}
while (fp1 != NULL)
{
notempty = (nxtc = nextc(fp1)) != '\n';
put_line(fp1);
if (nextc(fp1) == EOF)
fp1 = NULL;
if (printsingle && (printempty || notempty ))
printf(strf, bstring);
putchar('\n');
}
while (fp2 != NULL)
{
if (printsingle && (printempty || nextc(fp2) != '\n'))
printf(strf, bstring);
put_line(fp2);
if (nextc(fp2) == EOF)
fp2 = NULL;
putchar('\n');
}
} /* End of attachf.*/
/*----------------------------------------------------------------------*
* put_line -- Get a line, print a line. *
*----------------------------------------------------------------------*/
put_line(fp)
FILE *fp;
{
register int c;
while((c = getc(fp)) != '\n' && c != EOF)
putchar(c);
} /* End of put_line.*/
/*----------------------------------------------------------------------*
* nextc -- What is the next character? I realize that there are *
* some routines in some stdio.h files that do this for you, but *
* this is not true of all of them. Hence this function. *
*----------------------------------------------------------------------*/
nextc(fp)
FILE *fp;
{
register int c;
c = getc(fp);
ungetc(c, fp);
return(c);
} /* End of nextc.*/
/*----------------------------------------------------------------------*
* exit_error -- Print out the appropriate error message for the *
* appropriate error, then exit. *
*----------------------------------------------------------------------*/
exit_error(errcode, details)
int errcode;
char *details;
{
extern char *error_msg[];
fprintf(stderr, error_msg[errcode], program_name, details);
exit (1);
} /* End of exit_error.*/